﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Wamm.AbstractDataAccess;
using Wamm.ApplicationExceptions;
using WAMM.Domain.Services.Person.Parameters;
using WAMM.Domain.Services.Photo;
using Wamm.Domain.Services.Photo.Parameters;
using WAMM.Domain.Services.Photo.Parameters;
using WAMM.Domain.Services.ViewModels;
using Wamm.Model.Entities;

namespace WAMM.Domain.Services.Person {
    public class PersonService : IPersonService {
        private readonly ISession _session;
        private readonly IPhotoService _photoService;

        public PersonService(ISession session, IPhotoService photoService) {
            _session = session;
            _photoService = photoService;
        }

        public GetAllPeopleResponse GetAllPeople(GetAllPeopleRequest request) {
            var response = new GetAllPeopleResponse();
            var people = _session.People.FindWhere(x => x.Archived == request.IncludedDeleted).ToList();
            response.People = AutoMapper.Mapper.Map<IList<Wamm.Model.Entities.Person>, IList<PersonServiceModel>>(people);
            return response;
        }

        public GetPersonResponse GetPerson(GetPersonRequest request) {
            var response = new GetPersonResponse();
            var person = _session.People.FindById(request.Id);
            response.Person = AutoMapper.Mapper.Map<Wamm.Model.Entities.Person, PersonServiceModel>(person);
            return response;
        }

        public GetPersonPhotoResponse GetPersonPhoto(GetPersonPhotoRequest request)
        {
            var photoSizeType = ValidatePhotoSizeType(request.SizeType);
            var response = new GetPersonPhotoResponse();
            
            var sizeType = photoSizeType.ToString();
            var personPhoto =
                _session.PersonPhotos.FindWhere(p => p.PersonId == request.Id && p.SizeType == sizeType).
                    SingleOrDefault();
            if (personPhoto != null)
            {
                response.Photo = personPhoto.Picture;
            }
            else
            {
                response.Photo = NoPhoto(photoSizeType);
            }
            return response;
        }

        private static PhotoConstants.Size ValidatePhotoSizeType(string sizeType) {
            PhotoConstants.Size photoSizeType;
            if (!Enum.TryParse(sizeType, true, out photoSizeType)) {
                throw new ArgumentException(String.Format("Unknow value for sizeType={0}", sizeType));
            }
            return photoSizeType;
        }

        private static byte[] NoPhoto(PhotoConstants.Size sizeType)
        {
            switch (sizeType)
            {
                case PhotoConstants.Size.Small:
                    {
                        return GetBytes(Wamm.Domain.Properties.Resources.SmallNoPicture);
                    }
                case PhotoConstants.Size.Medium:
                    {
                        return GetBytes(Wamm.Domain.Properties.Resources.MediumNoPicture);
                    }
                case PhotoConstants.Size.Large:
                    {
                        return GetBytes(Wamm.Domain.Properties.Resources.LargeNoPicture);
                    }
                case PhotoConstants.Size.Original:
                    {
                        return GetBytes(Wamm.Domain.Properties.Resources.LargeNoPicture);                       
                    }
                default:
                    {
                        throw new Exception(String.Format("Unexpected sizeType={0}", sizeType));
                    }
            }
        }

        private static byte[] GetBytes(Image img)
        {
            using (var ms = new System.IO.MemoryStream())
            {
                img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
                return ms.ToArray();
            }
        }

        public void DeletePerson(DeletePersonRequest request) {
            var errors = new List<RuleViolation>();
            _session.People.RemoveById(request.Id);
            _session.Commit(errors);
        }

        public void UpdatePerson(UpdatePersonRequest request) {
            var person = _session.People.FindById(request.Person.Id);
            var errors = new List<RuleViolation>();
            if (request.Person.LastUpdatedTstamp == null ||
                request.Person.LastUpdatedTstamp.Value.ToString() == 
                person.LastUpdated.ToString()) {
                person.FirstName = request.Person.FirstName;
                person.LastName = request.Person.LastName;
                person.Gender = request.Person.Gender;
                person.BirthDate = request.Person.BirthDate;
                _session.Commit(errors);
            }
            else {
                throw new DataUpdatedBySomeoneElseException();
            }
            if (errors.Any()) {
                throw new RulesException(errors);
            }
        }
        
        public void UpdatePicture(UpdatePictureRequest request) {
            var person = _session.People.FindById(request.PersonId);
            var errors = new List<RuleViolation>();
            if (person.PersonPhotos == null)
            {
                person.PersonPhotos = new List<PersonPhoto>();
            }
            else
            {
                RemovePhotos(person);
            }

            // Original
            SaveOriginal(
                picture: request.PictureBytes,
                personPhotos: person.PersonPhotos,
                session: _session);

            // Small
            SaveResized(
                sizeType: Wamm.Model.Entities.PhotoConstants.Size.Small,
                width: 100,
                height: 100,
                pictureBytes: request.PictureBytes, 
                personPhotos: person.PersonPhotos,
                session: _session);

            // Medium
            SaveResized(
               sizeType: Wamm.Model.Entities.PhotoConstants.Size.Medium,
               width: 500,
               height: 500,
               pictureBytes: request.PictureBytes,
               personPhotos: person.PersonPhotos,
                session: _session);

            // Large
            SaveResized(
                sizeType: Wamm.Model.Entities.PhotoConstants.Size.Large,
                width: 1000,
                height: 1000,
                pictureBytes: request.PictureBytes,
                personPhotos: person.PersonPhotos,
                session: _session);

            _session.Commit(errors);
        }

        private void SaveResized(Wamm.Model.Entities.PhotoConstants.Size sizeType, 
                                 int width, 
                                 int height, 
                                 byte[] pictureBytes, 
                                 ICollection<PersonPhoto> personPhotos,
                                 ISession session)
        {
            var resizedPhoto = GetResizedPhoto(
                originalPhoto: pictureBytes,
                width: width,
                height: height,
                sizeType: sizeType);

            session.PersonPhotos.Add(resizedPhoto);
            personPhotos.Add(resizedPhoto);
        }

        private void SaveOriginal(byte[] picture, 
                                  ICollection<PersonPhoto> personPhotos,
                                  ISession session)
        {
            var getDimensionsResponse = _photoService.GetDimensions(new GetDimensionsRequest {Photo = picture});

            var original = new PersonPhoto
                               {
                                   SizeType = Wamm.Model.Entities.PhotoConstants.Size.Original.ToString(),
                                   Picture = picture,
                                   Width = getDimensionsResponse.Width,
                                   Height = getDimensionsResponse.Height
                               };

                session.PersonPhotos.Add(original);
                personPhotos.Add(original);
        }

        private PersonPhoto GetResizedPhoto(byte[] originalPhoto, int height, int width, Wamm.Model.Entities.PhotoConstants.Size sizeType)
        {
            var resizeRequest = new ResizeRequest
                                    {
                                        Photo = originalPhoto,
                                        Width = height,
                                        Height = width
                                    };

            var resizeResponse = _photoService.Resize(resizeRequest);

            return new PersonPhoto
                       {
                           SizeType = sizeType.ToString(),
                           Picture = resizeResponse.Photo,
                           Width = resizeResponse.Width,
                           Height = resizeResponse.Height
                       };
        }

        private void RemovePhotos(Wamm.Model.Entities.Person person)
        {
            var personPhotos = person.PersonPhotos.ToList();
            foreach (var personPhoto in personPhotos)
            {
                _session.PersonPhotos.Remove(personPhoto);
            }
        }

        public void CreatePerson(CreatePersonRequest request) {
            var person = new Wamm.Model.Entities.Person {
                FirstName = request.Person.FirstName,
                LastName = request.Person.LastName,
                Gender = request.Person.Gender,
                BirthDate = request.Person.BirthDate};
            _session.People.Add(person);
            var errors = new List<RuleViolation>();
            _session.Commit(errors);
            if (errors.Any()) {
                throw new RulesException(errors);
            }
        }

        public void Dispose() {
            if (_session != null) {
                _session.Dispose();
            }
        }
    }
}
